home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / DistUpgrade / DistUpgradeViewNonInteractive.py < prev    next >
Text File  |  2009-11-02  |  14KB  |  343 lines

  1. # DistUpgradeView.py 
  2. #  
  3. #  Copyright (c) 2004,2005 Canonical
  4. #  
  5. #  Author: Michael Vogt <michael.vogt@ubuntu.com>
  6. #  This program is free software; you can redistribute it and/or 
  7. #  modify it under the terms of the GNU General Public License as 
  8. #  published by the Free Software Foundation; either version 2 of the
  9. #  License, or (at your option) any later version.
  10. #  This program is distributed in the hope that it will be useful,
  11. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #  GNU General Public License for more details.
  14. #  You should have received a copy of the GNU General Public License
  15. #  along with this program; if not, write to the Free Software
  16. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  17. #  USA
  18.  
  19. import apt
  20. import logging
  21. import time
  22. import sys
  23. from DistUpgradeView import DistUpgradeView, InstallProgress, FetchProgress
  24. from DistUpgradeConfigParser import DistUpgradeConfig
  25. import os
  26. import pty
  27. import apt_pkg
  28. import select
  29. import fcntl
  30. import string
  31. import re
  32. import subprocess
  33. from subprocess import call, PIPE, Popen
  34. import copy
  35. import apt.progress
  36. from ConfigParser import NoSectionError, NoOptionError
  37.  
  38. class NonInteractiveFetchProgress(FetchProgress):
  39.     def updateStatus(self, uri, descr, shortDescr, status):
  40.         FetchProgress.updateStatus(self, uri, descr, shortDescr, status)
  41.         #logging.debug("Fetch: updateStatus %s %s" % (uri, status))
  42.         if status == apt.progress.FetchProgress.dlDone:
  43.             print "fetched %s (%.2f/100) at %sb/s" % (uri, self.percent, 
  44.                                                       apt_pkg.SizeToStr(int(self.currentCPS)))
  45.             if sys.stdout.isatty():
  46.                 sys.stdout.flush()
  47.         
  48.  
  49. class NonInteractiveInstallProgress(InstallProgress):
  50.     def __init__(self, logdir):
  51.         InstallProgress.__init__(self)
  52.         logging.debug("setting up environ for non-interactive use")
  53.         os.environ["DEBIAN_FRONTEND"] = "noninteractive"
  54.         os.environ["APT_LISTCHANGES_FRONTEND"] = "none"
  55.         os.environ["RELEASE_UPRADER_NO_APPORT"] = "1"
  56.         self.config = DistUpgradeConfig(".")
  57.         self.logdir = logdir
  58.         self.install_run_number = 0
  59.         try:
  60.             if self.config.getboolean("NonInteractive","ForceOverwrite"):
  61.                 apt_pkg.Config.Set("DPkg::Options::","--force-overwrite")
  62.         except (NoSectionError, NoOptionError), e:
  63.             pass
  64.         # more debug
  65.         #apt_pkg.Config.Set("Debug::pkgOrderList","true")
  66.         #apt_pkg.Config.Set("Debug::pkgDPkgPM","true")
  67.         # default to 2400 sec timeout
  68.         self.timeout = 2400
  69.         try:
  70.             self.timeout = self.config.getint("NonInteractive","TerminalTimeout")
  71.         except Exception, e:
  72.             pass
  73.     
  74.     def error(self, pkg, errormsg):
  75.         # re-run maintainer script with sh -x/perl debug to get a better 
  76.         # idea what went wrong
  77.         # 
  78.         # FIXME: this is just a approximation for now, we also need
  79.         #        to pass:
  80.         #        - a version after remove (if upgrade to new version)
  81.         #
  82.         #        not everything is a shell or perl script
  83.         #
  84.         # if the new preinst fails, its not yet in /var/lib/dpkg/info
  85.         # so this is inaccurate as well
  86.         logging.error("got a error from dpkg for pkg: '%s': '%s'" % (pkg, errormsg))
  87.         if not self.config.getboolean("NonInteractive","DebugBrokenScripts"):
  88.             return
  89.         environ = copy.copy(os.environ)
  90.         environ["PYCENTRAL"] = "debug"
  91.         cmd = []
  92.  
  93.         # find what maintainer script failed
  94.         if "post-installation" in errormsg:
  95.             prefix = "/var/lib/dpkg/info/"
  96.             name = "postinst"
  97.             argument = "configure"
  98.             maintainer_script = "%s/%s.%s" % (prefix, pkg, name)
  99.         elif "pre-installation" in errormsg:
  100.             prefix = "/var/lib/dpkg/tmp.ci/"
  101.             #prefix = "/var/lib/dpkg/info/"
  102.             name = "preinst"
  103.             argument = "install"
  104.             maintainer_script = "%s/%s" % (prefix, name)
  105.         elif "pre-removal" in errormsg:
  106.             prefix = "/var/lib/dpkg/info/"
  107.             name = "prerm"
  108.             argument = "remove"
  109.             maintainer_script = "%s/%s.%s" % (prefix, pkg, name)
  110.         elif "post-removal" in errormsg:
  111.             prefix = "/var/lib/dpkg/info/"
  112.             name = "postrm"
  113.             argument = "remove"
  114.             maintainer_script = "%s/%s.%s" % (prefix, pkg, name)
  115.         else:
  116.             print "UNKNOWN (trigger?) dpkg/script failure for %s (%s) " % (pkg, errormsg)
  117.             return
  118.  
  119.         # find out about the interpreter
  120.         if not os.path.exists(maintainer_script):
  121.             logging.error("can not find failed maintainer script '%s' " % maintainer_script)
  122.             return
  123.         interp = open(maintainer_script).readline()[2:].strip().split()[0]
  124.         if ("bash" in interp) or ("/bin/sh" in interp):
  125.             debug_opts = ["-ex"]
  126.         elif ("perl" in interp):
  127.             debug_opts = ["-d"]
  128.             environ["PERLDB_OPTS"] = "AutoTrace NonStop"
  129.         else:
  130.             logging.warning("unknown interpreter: '%s'" % interp)
  131.  
  132.         # check if debconf is used and fiddle a bit more if it is
  133.         if ". /usr/share/debconf/confmodule" in open(maintainer_script).read():
  134.             environ["DEBCONF_DEBUG"] = "developer"
  135.             environ["DEBIAN_HAS_FRONTEND"] = "1"
  136.             interp = "/usr/share/debconf/frontend"
  137.             debug_opts = ["sh","-ex"]
  138.  
  139.         # build command
  140.         cmd.append(interp)
  141.         cmd.extend(debug_opts)
  142.         cmd.append(maintainer_script)
  143.         cmd.append(argument)
  144.  
  145.         # check if we need to pass a version
  146.         if name == "postinst":
  147.             version = Popen("dpkg-query -s %s|grep ^Config-Version" % pkg,shell=True, stdout=PIPE).communicate()[0]
  148.             if version:
  149.                 cmd.append(version.split(":",1)[1].strip())
  150.         elif name == "preinst":
  151.             pkg = os.path.basename(pkg)
  152.             pkg = pkg.split("_")[0]
  153.             version = Popen("dpkg-query -s %s|grep ^Version" % pkg,shell=True, stdout=PIPE).communicate()[0]
  154.             if version:
  155.                 cmd.append(version.split(":",1)[1].strip())
  156.  
  157.         print cmd
  158.         logging.debug("re-running '%s' (%s)" % (cmd, environ))
  159.         ret = subprocess.call(cmd, env=environ)
  160.         logging.debug("%s script returned: %s" % (name,ret))
  161.         
  162.     def conffile(self, current, new):
  163.         logging.warning("got a conffile-prompt from dpkg for file: '%s'" % current)
  164.         # looks like we have a race here *sometimes*
  165.         time.sleep(5)
  166.     try:
  167.           # don't overwrite
  168.       os.write(self.master_fd,"n\n")
  169.      except Exception, e:
  170.       logging.error("error '%s' when trying to write to the conffile"%e)
  171.  
  172.     def startUpdate(self):
  173.         InstallProgress.startUpdate(self)
  174.         self.last_activity = time.time()
  175.         progress_log = self.config.getboolean("NonInteractive","DpkgProgressLog")
  176.         if progress_log:
  177.             fullpath = os.path.join(self.logdir, "dpkg-progress.%s.log" % self.install_run_number)
  178.             logging.debug("writing dpkg progress log to '%s'" % fullpath)
  179.             self.dpkg_progress_log = open(fullpath, "w")
  180.         else:
  181.             self.dpkg_progress_log = open(os.devnull, "w")
  182.         self.dpkg_progress_log.write("%s: Start\n" % time.time())
  183.     def finishUpdate(self):
  184.         InstallProgress.finishUpdate(self)
  185.         self.dpkg_progress_log.write("%s: Finished\n" % time.time())
  186.         self.dpkg_progress_log.close()
  187.         self.install_run_number += 1
  188.     def statusChange(self, pkg, percent, status_str):
  189.         self.dpkg_progress_log.write("%s:%s:%s:%s\n" % (time.time(),
  190.                                                         percent,
  191.                                                         pkg,
  192.                                                         status_str))
  193.     def updateInterface(self):
  194.         if self.statusfd == None:
  195.             return
  196.  
  197.         if (self.last_activity + self.timeout) < time.time():
  198.             logging.warning("no activity %s seconds (%s) - sending ctrl-c" % (self.timeout, self.status))
  199.             # ctrl-c
  200.             os.write(self.master_fd,chr(3))
  201.  
  202.  
  203.         # read status fd from dpkg
  204.         # from python-apt/apt/progress.py (but modified a bit)
  205.         # -------------------------------------------------------------
  206.         res = select.select([self.statusfd],[],[],0.1)
  207.         while len(res[0]) > 0:
  208.             self.last_activity = time.time()
  209.             while not self.read.endswith("\n"):
  210.                 self.read += os.read(self.statusfd.fileno(),1)
  211.             if self.read.endswith("\n"):
  212.                 s = self.read
  213.                 #print s
  214.                 (status, pkg, percent, status_str) = string.split(s, ":")
  215.                 if status == "pmerror":
  216.                     self.error(pkg,status_str)
  217.                 elif status == "pmconffile":
  218.                     # we get a string like this:
  219.                     # 'current-conffile' 'new-conffile' useredited distedited
  220.                     match = re.compile("\s*\'(.*)\'\s*\'(.*)\'.*").match(status_str)
  221.                     if match:
  222.                         self.conffile(match.group(1), match.group(2))
  223.                 elif status == "pmstatus":
  224.                     if (float(percent) != self.percent or 
  225.                         status_str != self.status):
  226.                         self.statusChange(pkg, float(percent), status_str.strip())
  227.                         self.percent = float(percent)
  228.                         self.status = string.strip(status_str)
  229.                         sys.stdout.write("[%s] %s: %s\n" % (float(percent), pkg, status_str.strip()))
  230.                         sys.stdout.flush()
  231.             self.read = ""
  232.             res = select.select([self.statusfd],[],[],0.1)
  233.         # -------------------------------------------------------------
  234.  
  235.         #fcntl.fcntl(self.master_fd, fcntl.F_SETFL, os.O_NDELAY)
  236.         # read master fd (terminal output)
  237.         res = select.select([self.master_fd],[],[],0.1)
  238.         while len(res[0]) > 0:
  239.            self.last_activity = time.time()
  240.            try:
  241.                s = os.read(self.master_fd, 1)
  242.                sys.stdout.write("%s" % s)
  243.            except OSError,e:
  244.                # happens after we are finished because the fd is closed
  245.                return
  246.            res = select.select([self.master_fd],[],[],0.1)
  247.         sys.stdout.flush()
  248.  
  249.     def fork(self):
  250.         logging.debug("doing a pty.fork()")
  251.         # some maintainer scripts fail without
  252.         os.environ["TERM"] = "dumb"
  253.         (self.pid, self.master_fd) = pty.fork()
  254.         if self.pid != 0:
  255.             logging.debug("pid is: %s" % self.pid)
  256.         return self.pid
  257.         
  258.  
  259. class DistUpgradeViewNonInteractive(DistUpgradeView):
  260.     " non-interactive version of the upgrade view "
  261.     def __init__(self, datadir=None, logdir=None):
  262.         self.config = DistUpgradeConfig(".")
  263.         self._fetchProgress = NonInteractiveFetchProgress()
  264.         self._installProgress = NonInteractiveInstallProgress(logdir)
  265.         self._opProgress = apt.progress.OpProgress()
  266.         sys.__excepthook__ = self.excepthook
  267.     def excepthook(self, type, value, traceback):
  268.         " on uncaught exceptions -> print error and reboot "
  269.         logging.error("got exception '%s': %s " % (type, value))
  270.         #sys.excepthook(type, value, traceback)
  271.         self.confirmRestart()
  272.     def getOpCacheProgress(self):
  273.         " return a OpProgress() subclass for the given graphic"
  274.         return self._opProgress
  275.     def getFetchProgress(self):
  276.         " return a fetch progress object "
  277.         return self._fetchProgress
  278.     def getInstallProgress(self, cache=None):
  279.         " return a install progress object "
  280.         return self._installProgress
  281.     def updateStatus(self, msg):
  282.         """ update the current status of the distUpgrade based
  283.             on the current view
  284.         """
  285.         pass
  286.     def setStep(self, step):
  287.         """ we have 5 steps current for a upgrade:
  288.         1. Analyzing the system
  289.         2. Updating repository information
  290.         3. Performing the upgrade
  291.         4. Post upgrade stuff
  292.         5. Complete
  293.         """
  294.         pass
  295.     def confirmChanges(self, summary, changes, downloadSize,
  296.                        actions=None, removal_bold=True):
  297.         DistUpgradeView.confirmChanges(self, summary, changes, downloadSize, actions)
  298.     logging.debug("toinstall: '%s'" % self.toInstall)
  299.         logging.debug("toupgrade: '%s'" % self.toUpgrade)
  300.         logging.debug("toremove: '%s'" % self.toRemove)
  301.         return True
  302.     def askYesNoQuestion(self, summary, msg):
  303.         " ask a Yes/No question and return True on 'Yes' "
  304.         return True
  305.     def confirmRestart(self):
  306.         " generic ask about the restart, can be overridden "
  307.     logging.debug("confirmRestart() called")
  308.         # ignore if we don't have this option
  309.         try:
  310.             # rebooting here makes sense if we run e.g. in qemu
  311.             return self.config.getboolean("NonInteractive","RealReboot")
  312.         except Exception, e:
  313.             logging.debug("no RealReboot found, returning false (%s) " % e)
  314.             return False
  315.     def error(self, summary, msg, extended_msg=None):
  316.         " display a error "
  317.         logging.error("%s %s (%s)" % (summary, msg, extended_msg))
  318.     def abort(self):
  319.         logging.error("view.abort called")
  320.  
  321.  
  322. if __name__ == "__main__":
  323.  
  324.   view = DistUpgradeViewNonInteractive()
  325.   fp = NonInteractiveFetchProgress()
  326.   ip = NonInteractiveInstallProgress()
  327.  
  328.   #ip.error("linux-image-2.6.17-10-generic","post-installation script failed")
  329.   ip.error("xserver-xorg","pre-installation script failed")
  330.  
  331.   cache = apt.Cache()
  332.   for pkg in sys.argv[1:]:
  333.     #if cache[pkg].isInstalled:
  334.     #  cache[pkg].markDelete()
  335.     #else:
  336.     cache[pkg].markInstall()
  337.   cache.commit(fp,ip)
  338.   time.sleep(2)
  339.   sys.exit(0)
  340.